<?php

namespace App\Service\Kms\Attendance;

use App\Error\BaseError;
use App\Events\Kms\PunchShareEvent;
use App\Exceptions\KfHttpResponseException;
use App\Http\Resources\Kms\Attendance\DailyFieldGroupListResource;
use App\Http\Resources\Kms\Attendance\FieldGroupListResource;
use App\Http\Resources\Kms\Attendance\FieldListDetailResource;
use App\Http\Resources\Kms\Attendance\FieldListResource;
use App\Library\FeiShu\SendFs;
use App\Models\Kms\Album\AlbumResourceModel;
use App\Models\Kms\Attendance\AttendanceDailyGatherModel;
use App\Models\Kms\Attendance\AttendanceDailyPointTraceModel;
use App\Models\Kms\User\KmsUser;
use App\Models\Kms\User\KmsUserConfigModel;
use App\Models\OpenShop\InvestmentPromotion\OpenShopOrganizationManageModel;
use App\Models\OpenShop\InvestmentPromotion\OpenShopOrganizationManageUserModel;
use App\Remote\AMap\AMapPlaceRemote;
use App\Remote\AMap\Track\ServiceRemote;
use App\Remote\AMap\Track\TerminalRemote;
use App\Remote\Feishu\FeishuImageRemote;
use App\Service\Kms\Album\AlbumMainService;
use App\Service\Kms\Common\DateService;
use App\Service\Kms\KmsBaseService;
use App\Service\Kms\System\SystemService;
use App\Service\OpenShop\Feishu\ConfigService;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class AttendanceService extends KmsBaseService
{
    /**
     * @param object $params
     * @return void
     * @throws ValidationException
     */
    public static function dailyPointTraceUpload(object $params)
    {
        $attendanceConfig              = ConfigService::getConfigDetail("kms.user", "attendance")["value"]["field"];
        $nowTimestamp                  = time();
        $attendanceConfig["startTime"] = (int)(str_replace(":", "", $attendanceConfig["startTimeFormat"]));
        $attendanceConfig["endTime"]   = (int)(str_replace(":", "", $attendanceConfig["endTimeFormat"]));

        $uid = request()->user()->rmsUserId;

        $tracePointsCollection = collect($params->tracePoints)->map(function (array $point) {
            $point["tracePointTime"] = strtotime($point["tracePointTime"]);
            $point["tracePointDate"] = date("Y-m-d", $point["tracePointTime"]);
            return $point;
        })->values()->sortBy("tracePointTime");

        $dailyGatherModelGroup = $tracePointsCollection->pluck("tracePointDate")->map(function ($date) use (
            $nowTimestamp,
            $uid
        ) {
            return AttendanceDailyGatherModel::query()->firstOrCreate(
                [
                    "userId" => request()->user()->rmsUserId,
                    "date"   => $date
                ],
                [
                    "createdAt"    => $nowTimestamp,
                    "createdByUid" => $uid,
                    "updatedAt"    => $nowTimestamp,
                    "updatedByUid" => $uid
                ]
            );
        })->values();

        $lastPointModel = AttendanceDailyPointTraceModel::query()->where("userId", $uid)
                                                        ->where("status", ">", AttendanceDailyPointTraceModel::STATUS_0)
                                                        ->orderByDesc("id")->first();
        if ($lastPointModel) {
            $lastPoint                   = $lastPointModel->toArray();
            $lastPoint["tracePointDate"] = date("Y-m-d", $lastPoint["tracePointTime"]);
        } else {
            $lastPoint = null;
        }

        $status      = AttendanceDailyGatherModel::STATUS_NORMAL;
        $tracePoints = $tracePointsCollection->map(function (array $point) use (
            &$lastPoint,
            &$status,
            $attendanceConfig,
            $dailyGatherModelGroup
        ) {
            return self::buildPointData($dailyGatherModelGroup, $attendanceConfig, $status, $point,
                $lastPoint);
        })->toArray();

        AttendanceDailyPointTraceModel::query()->insert($tracePoints);
        $dailyGatherModelGroup->each(function (AttendanceDailyGatherModel $dailyGatherModel) use ($attendanceConfig) {
            $startTime    = strtotime($dailyGatherModel->date . " " . $attendanceConfig["startTimeFormat"]);
            $endTime      = strtotime($dailyGatherModel->date . " " . $attendanceConfig["endTimeFormat"]);
            $errorSumTime = AttendanceDailyPointTraceModel::query()
                                                          ->where("userId", $dailyGatherModel->userId)
                                                          ->where("tracePointTime", ">=", $startTime)
                                                          ->where("tracePointTime", "<=", $endTime)
                                                          ->whereIn("status", [
                                                              AttendanceDailyPointTraceModel::STATUS_4,
                                                              AttendanceDailyPointTraceModel::STATUS_5,
                                                              AttendanceDailyPointTraceModel::STATUS_1,
                                                          ])->sum("lastPointTime");

            $normalSumTime = AttendanceDailyPointTraceModel::query()
                                                           ->where("userId", $dailyGatherModel->userId)
                                                           ->where("tracePointTime", ">=", $startTime)
                                                           ->where("tracePointTime", "<=", $endTime)
                                                           ->whereIn("status", [
                                                               AttendanceDailyPointTraceModel::STATUS_2,
                                                               AttendanceDailyPointTraceModel::STATUS_3
                                                           ])->sum("lastPointTime");

            $totalDistance = AttendanceDailyPointTraceModel::query()
                                                           ->where("userId", $dailyGatherModel->userId)
                                                           ->where("tracePointTime", ">=",
                                                               $startTime)
                                                           ->where("tracePointTime", "<=", $endTime)
                                                           ->where("status", ">",
                                                               AttendanceDailyPointTraceModel::STATUS_0)
                                                           ->sum("lastPointDistance");


            $dailyGatherModel->traceDistance  = $totalDistance;
            $dailyGatherModel->missDuration   = $errorSumTime;
            $dailyGatherModel->normalDuration = $normalSumTime;
            if ($errorSumTime >= $attendanceConfig["uploadConfig"]["totalMissTime"]) {
                $dailyGatherModel->status = AttendanceDailyGatherModel::STATUS_ERROR;
            }
            $dailyGatherModel->save();
        });
    }


    /**
     * @param Collection $dailyGatherModelGroup
     * @param array $attendanceConfig
     * @param int $status
     * @param array $nowPoint
     * @param array|null $lastPoint
     * @return array
     * @throws GuzzleException
     * @throws ValidationException
     */
    public static function buildPointData(
        Collection $dailyGatherModelGroup,
        array $attendanceConfig,
        int &$status,
        array $nowPoint,
        array &$lastPoint = null
    ): array {
        $distance          = 0;
        $address           = "";
        $reportingInterval = 0;
        $getStatusFunc     = function () use (
            $lastPoint,
            $attendanceConfig,
            $nowPoint,
            &$distance,
            &$address,
            &$status,
            &$reportingInterval
        ) {
            if ($nowPoint["longitude"] && $nowPoint["latitude"]) {
                try {
                    $address = AMapPlaceRemote::reGeo($nowPoint["longitude"],
                            $nowPoint["latitude"])['regeocode']['formatted_address'] ?? "";
                } catch (\Exception $exception) {
                }
            }

            if (empty($lastPoint) || $lastPoint["tracePointDate"] != $nowPoint["tracePointDate"]) {
                $reportingInterval = $nowPoint["tracePointTime"] - strtotime($nowPoint["tracePointDate"] . " " . $attendanceConfig["startTimeFormat"]);
                return AttendanceDailyPointTraceModel::STATUS_1;
            }

            $reportingInterval = $nowPoint["tracePointTime"] - $lastPoint["tracePointTime"];

            if ($reportingInterval <= 0) {
                return AttendanceDailyPointTraceModel::STATUS_0;
            }

            if ((empty($lastPoint["longitude"]) || empty($lastPoint["latitude"]))) {
                return AttendanceDailyPointTraceModel::STATUS_1;
            }

            if (empty($nowPoint["longitude"]) || empty($nowPoint["latitude"])) {
                return AttendanceDailyPointTraceModel::STATUS_5;
            }

            $distance = calcDistance(
                $nowPoint["longitude"],
                $nowPoint["latitude"],
                $lastPoint["longitude"],
                $lastPoint["latitude"]
            );

            if ($reportingInterval > $attendanceConfig["uploadConfig"]["maxReportingInterval"]) {
                return AttendanceDailyPointTraceModel::STATUS_4;
            }


            $normalAvgRate   = sprintf("%.2f",
                $attendanceConfig["statusConfig"]["radius"] / $attendanceConfig["statusConfig"]["time"]);
            $actuallyAvgRate = sprintf("%.2f", $distance / $reportingInterval);
            if ($actuallyAvgRate > $normalAvgRate) {
                return AttendanceDailyPointTraceModel::STATUS_2;
            }
            return AttendanceDailyPointTraceModel::STATUS_3;
        };

        $pointStatus = $getStatusFunc();
        if ($reportingInterval > 0 || empty($lastPoint)) {
            $lastPoint = $nowPoint;
        }

        $date             = date("Y-m-d", $nowPoint["tracePointTime"]);
        $dailyGatherModel = $dailyGatherModelGroup->where("date", $date)->first();
        return array_merge(
            [
                "tracePointTime"        => $nowPoint["tracePointTime"],
                "gpsSwitch"             => $nowPoint["gpsSwitch"],
                "wifiSwitch"            => $nowPoint["wifiSwitch"],
                "cellularNetworkSwitch" => $nowPoint["cellularNetworkSwitch"],
                "dumpEnergy"            => $nowPoint["dumpEnergy"],
                "longitude"             => $nowPoint["longitude"],
                "latitude"              => $nowPoint["latitude"],
                "deviceModel"           => $nowPoint["deviceModel"],
                "deviceSerialNumber"    => $nowPoint["deviceSerialNumber"],
                "deviceSystem"          => $nowPoint["deviceSystem"],
                "extra"                 => empty($nowPoint["extra"]) ? null : json_encode(array_merge($nowPoint["extra"],
                    ["version" => request()->header("version", "")]), true),
            ], [
            "dailyGatherId"     => $dailyGatherModel->id,
            "userId"            => request()->user()->rmsUserId,
            "date"              => $date,
            "status"            => $pointStatus,
            "address"           => is_array($address) ? "" : $address,
            "lastPointDistance" => $distance,
            "lastPointTime"     => $reportingInterval,
            "createdAt"         => time(),
            "createdByUid"      => request()->user()->rmsUserId
        ]);
    }


    /**
     * @param object $params
     * @return array[]
     */
    public static function dailyFieldGroupList(object $params): array
    {

        $userIds      = null;
        $uid          = attributeGet($params, "uid", 0);
        $departmentId = attributeGet($params, "departmentId", "");
        $jobTitle     = attributeGet($params, "jobTitle", "");
        $mobile       = attributeGet($params, "mobile", "");
        $startDate    = attributeGet($params, "startDate", "");
        $endDate      = attributeGet($params, "endDate", "");
        $pageSize     = attributeGet($params, "pageSize", 10);
        $pageNum      = attributeGet($params, "pageNum", 1);
        $sortField    = attributeGet($params, "sortField", "");
        $sortOrder    = attributeGet($params, "sortOrder", "");

        if ($uid || $departmentId || $jobTitle || $mobile) {
            $userIds = KmsUser::query()
                              ->where("rmsUserId", ">", 0)
                              ->when($uid > 0, function ($query) use ($uid) {
                                  $query->where("rmsUserId", $uid);
                              })->when($departmentId, function ($query) use ($departmentId) {
                    $query->where("rootDepartmentId", $departmentId);
                })->when($jobTitle, function ($query) use ($jobTitle) {
                    $query->where("jobTitle", $jobTitle);
                })->when($mobile, function ($query) use ($mobile) {
                    $query->where("mobile", $mobile);
                })->pluck("rmsUserId")->toArray();
        }


        $paginate = AttendanceDailyGatherModel::query()
                                              ->with("userRel")
                                              ->when(!is_null($userIds), function ($query) use ($userIds) {
                                                  $query->whereIn("userId", $userIds);
                                              })
                                              ->when(
                                                  $startDate,
                                                  function ($query) use ($startDate) {
                                                      $query->where("date", ">=", $startDate);
                                                  })
                                              ->when(
                                                  $endDate,
                                                  function ($query) use ($endDate) {
                                                      $query->where("date", "<=", $endDate);
                                                  })
                                              ->where("userId", ">", 0)
                                              ->groupBy(["userId"])
                                              ->select([
                                                  "userId",
                                                  DB::raw("COUNT(IF(FIELD( status, '" . AttendanceDailyGatherModel::STATUS_NO_TRACE . "' ) = 1, 1, NULL)) missDays"),
                                                  DB::raw("COUNT(IF(FIELD( status, '" . AttendanceDailyGatherModel::STATUS_ERROR . "' ) = 1, 1, NULL)) abnormalDays"),
                                                  DB::raw("COUNT(IF(FIELD( status, '" . AttendanceDailyGatherModel::STATUS_NORMAL . "' ) = 1, 1, NULL)) normalDays"),
                                              ])
                                              ->when($sortField, function ($query) use ($sortField, $sortOrder) {
                                                  $query->orderBy($sortField, $sortOrder == "ascend" ? "asc" : "desc");
                                              })
                                              ->orderBy("userId")
                                              ->paginate(
                                                  $pageSize,
                                                  ['*'],
                                                  'pageNum',
                                                  $pageNum
                                              );


        return [
            "list" => [
                "total"    => $paginate->total(),
                "pageNum"  => $paginate->currentPage(),
                "pageSize" => $pageSize,
                "list"     => DailyFieldGroupListResource::collection($paginate->items())
            ]
        ];
    }

    /**
     * @param object $params
     * @return array[]
     */
    public static function fieldList(object $params): array
    {
        $pagination = AttendanceDailyGatherModel::query()->with(
            ["userRel"]
        )->when(attributeGet($params, "uid", 0), function ($query) use ($params) {
            $query->where("userId", $params->uid);
        })->when(attributeGet($params, "startDate", ""), function ($query) use ($params) {
            $query->where("date", ">=", $params->startDate);
        })->when(attributeGet($params, "endDate", ""), function ($query) use ($params) {
            $query->where("date", "<=", $params->endDate);
        })->when(!is_null(($status = attributeGet($params, "status", null))), function ($query) use ($status) {
            $query->where("status", $status);
        })->paginate(
            ($pageSize = attributeGet($params, "pageSize", 10)),
            ['*'],
            'pageNum',
            ($pageNum = attributeGet($params, "pageNum", 1))
        );

        if (count($pagination->items())) {
            $withCount = AttendanceDailyPointTraceModel::query()->whereIn("dailyGatherId",
                collect($pagination->items())->pluck("id")->toArray())->groupBy(["dailyGatherId", "deviceSerialNumber"])
                                                       ->select(["dailyGatherId", "deviceSerialNumber"])->get();
        } else {
            $withCount = collect([]);
        }
        request()->withCount = $withCount;
        return [
            "list" => [
                "total"    => $pagination->total(),
                "pageNum"  => $pagination->currentPage(),
                "pageSize" => $pageSize,
                "list"     => FieldListResource::collection($pagination->items())
            ]
        ];
    }

    /**
     * @param object $params
     * @return array
     */
    public static function fieldTraceDetailList(object $params): array
    {
        $list = AttendanceDailyPointTraceModel::query()
                                              ->where("userId", $params->uid)
                                              ->where("date", $params->date)
                                              ->orderBy("id")->get();
        return [
            "list" => FieldListDetailResource::collection($list)
        ];
    }

    /**
     * @param object $params
     * @return array[]
     * @throws ValidationException
     */
    public static function fieldTrace(object $params): array
    {
        $dailyGatherModel = AttendanceDailyGatherModel::query()
                                                      ->where("userId", $params->uid)
                                                      ->where("date", $params->date)
                                                      ->first();

        $trace = [];
        if (!empty($dailyGatherModel)) {
            $attendanceConfig = ConfigService::getConfigDetail("kms.user", "attendance")["value"]["field"];
            $startTime        = strtotime($dailyGatherModel->date . " " . $attendanceConfig["startTimeFormat"]);
            $endTime          = strtotime($dailyGatherModel->date . " " . $attendanceConfig["endTimeFormat"]);
            $list             = AttendanceDailyPointTraceModel::query()
                                                              ->where("dailyGatherId", $dailyGatherModel->id)
                                                              ->where("tracePointTime", ">=", $startTime)
                                                              ->where("tracePointTime", "<=", $endTime)
                                                              ->where("status", ">",
                                                                  AttendanceDailyPointTraceModel::STATUS_0)
                                                              ->orderBy("id")
                                                              ->get();

            $quoteGroup = [];
            $lastPoint  = [];
            $tempTrace  = [];
            $count      = $list->count();
            $list->each(function (
                AttendanceDailyPointTraceModel $pointTraceModel,
                $key
            ) use (
                $count,
                &$quote,
                &$lastStatus,
                &$lastPoint,
                &$pokeTrace,
                &$quoteGroup,
                &$tempTrace
            ) {
                $pointTraceModel->tracePointTime = formatDate($pointTraceModel->tracePointTime);
                if (($lastPoint && $pointTraceModel->status != $lastPoint->status)) {
                    array_push($tempTrace, $quoteGroup);
                    $quoteGroup = [];
                }
                array_push($quoteGroup, $pointTraceModel->toArray());
                $lastPoint = $pointTraceModel;
            });

            if ($quoteGroup) {
                array_push($tempTrace, $quoteGroup);
            }

            $lastTrace = [];
            $trace     = collect($tempTrace)->map(function ($trace) use (&$lastTrace) {
                $traceGroup  = collect($trace);
                $endTrace    = $traceGroup->last();
                $returnTrace = [];
                if (!empty($lastTrace)) {
                    $returnTrace = [
                        "point"    => $traceGroup->push($lastTrace)->sortBy("tracePointTime")->map(function ($point) {
                            return collect($point)->only([
                                "id",
                                "tracePointTime",
                                "longitude",
                                "latitude"
                            ])->toArray();
                        })->values()->toArray(),
                        "begin"    => [
                            "id"             => $lastTrace["id"],
                            "tracePointTime" => $lastTrace["tracePointTime"],
                            "longitude"      => $lastTrace["longitude"],
                            "latitude"       => $lastTrace["latitude"],
                            "dumpEnergy"     => $lastTrace["dumpEnergy"],
                            "status"         => $lastTrace["status"],
                            "address"        => $lastTrace["address"],
                            "statusText"     => AttendanceDailyPointTraceModel::STATUS_TEXT[$lastTrace["status"]],
                        ],
                        "end"      => [
                            "id"             => $endTrace["id"],
                            "tracePointTime" => $endTrace["tracePointTime"],
                            "longitude"      => $endTrace["longitude"],
                            "latitude"       => $endTrace["latitude"],
                            "dumpEnergy"     => $endTrace["dumpEnergy"],
                            "status"         => $endTrace["status"],
                            "address"        => $endTrace["address"],
                            "statusText"     => AttendanceDailyPointTraceModel::STATUS_SHOW_TEXT[$endTrace["status"]],
                        ],
                        "ids"      => $traceGroup->pluck("id")->implode(","),
                        "time"     => strtotime($endTrace["tracePointTime"]) - strtotime($lastTrace["tracePointTime"]),
                        "distance" => collect($trace)->sum("lastPointDistance")
                    ];
                }
                $lastTrace = $endTrace;
                return $returnTrace;
            })->values()->filter()->values()->toArray();
        }

        return [
            "trace" => $trace
        ];
    }

    /**
     * @param AttendanceDailyGatherModel $dailyGatherModel
     * @return array
     * @throws ValidationException
     */
    public static function getAttendanceFieldConfig(AttendanceDailyGatherModel $dailyGatherModel): array
    {
        $attendanceConfig = ConfigService::getConfigDetail(
            "kms.user",
            "attendance"
        )["value"]["field"];

        $startTime = strtotime($dailyGatherModel->date . " " . $attendanceConfig["startTimeFormat"]);
        $endTime   = strtotime($dailyGatherModel->date . " " . $attendanceConfig["endTimeFormat"]);
//        $startTime = strtotime($dailyGatherModel->date . " " . "08:00");
//        $endTime   = strtotime($dailyGatherModel->date . " " . "22:00");

        $normalAvgRate = sprintf(
            "%.2f",
            $attendanceConfig["statusConfig"]["radius"] / $attendanceConfig["statusConfig"]["time"]
        );
        //上传最大时间
        $maxReportingInterval = $attendanceConfig["uploadConfig"]["maxReportingInterval"];

        //精度
        $accuracy = $attendanceConfig["accuracy"];
        return [$startTime, $endTime, $normalAvgRate, $maxReportingInterval, $accuracy];
    }

    /**
     * 校验是否为躁点数据
     *
     * @param AttendanceDailyPointTraceModel $tracePointModel
     * @param int $accuracy
     */
    public static function checkExcursionPoint(AttendanceDailyPointTraceModel &$tracePointModel, int $accuracy)
    {
        $tracePointModel->isExcursionPoint = false;
        if (strlen($tracePointModel->extra) > 0) {
            $extra                             = json_decode($tracePointModel->extra, true);
            $tracePointModel->locationType     = attributeGet($extra, "locationType", null);
            $tracePointModel->locationTypeText = AttendanceDailyPointTraceModel::LOCATION_TYPE_TEXT[$tracePointModel->locationType] ?? null;
            $tracePointModel->accuracy         = attributeGet($extra, "accuracy", 0);
            if (
//                in_array($tracePointModel->locationType, [
//                    AttendanceDailyPointTraceModel::LOCATION_TYPE_2,
//                    AttendanceDailyPointTraceModel::LOCATION_TYPE_4,
//                    AttendanceDailyPointTraceModel::LOCATION_TYPE_0,
//                    AttendanceDailyPointTraceModel::LOCATION_TYPE_9
//                ])
//                ||
                $tracePointModel->accuracy >= $accuracy
            ) {
                $tracePointModel->isExcursionPoint = true;
            }
        }
    }

    /**
     * 合并躁点 重构轨迹点位
     *
     * @param Collection $attendanceDailyPointTraceCollection
     * @param int $accuracy
     * @return Collection
     */
    public static function mergeExcursionPoint(
        Collection $attendanceDailyPointTraceCollection,
        int $accuracy
    ): Collection {
        $excursionPointGroup     = [];
        $tempExcursionPointGroup = [];
        $attendanceDailyPointTraceCollection->each(function (AttendanceDailyPointTraceModel &$tracePointModel) use (
            &$excursionPointGroup,
            &$tempExcursionPointGroup,
            $accuracy
        ) {
            self::checkExcursionPoint($tracePointModel, $accuracy);
            array_push($tempExcursionPointGroup, $tracePointModel);
            if (!$tracePointModel->isExcursionPoint) {
                array_push($excursionPointGroup, $tempExcursionPointGroup);
                $tempExcursionPointGroup = [];
            }
        });
        if (!empty($tempExcursionPointGroup)) {
            $lastIndex = count($excursionPointGroup) - 1;
            if ($lastIndex >= 0) {
                $excursionPointGroup[$lastIndex] = array_merge(
                    $excursionPointGroup[$lastIndex],
                    $tempExcursionPointGroup
                );
            } else {
                array_push($excursionPointGroup, $tempExcursionPointGroup);
            }
        }
        return collect($excursionPointGroup);
    }

    /**
     * 聚合躁点（时间&点位）
     *
     * @param Collection $mergeExcursionPointCollect
     * @return Collection
     */
    public static function groupExcursionPoint(Collection $mergeExcursionPointCollect): Collection
    {
        return $mergeExcursionPointCollect->map(function ($excursionPoint) {
            $excursionPointCollect = collect($excursionPoint);
            $point                 = $excursionPointCollect->where("isExcursionPoint", false)->first();
            if (empty($point)) {
                $point = $excursionPointCollect->first();
            }
            $tracePointTimeGroup      = $excursionPointCollect->pluck("tracePointTime");
            $point->minTracePointTime = $tracePointTimeGroup->min();
            $point->maxTracePointTime = $tracePointTimeGroup->max();
            $point->groupIds          = $excursionPointCollect->pluck("id")->implode(",");
            return $point;
        })->values();
    }

    /**
     * 确认轨迹状态
     *
     * @param Collection $groupExcursionPoint
     * @param int $maxReportingInterval
     * @param int $normalAvgRate
     * @return Collection
     */
    public static function confirmPointStatus(
        Collection $groupExcursionPoint,
        int $maxReportingInterval,
        int $normalAvgRate
    ): Collection {
        $continueFunc = function (
            AttendanceDailyPointTraceModel $nowPoint,
            int $status,
            AttendanceDailyPointTraceModel &$lastPoint = null
        ) {
            $nowPoint->status = $status;
            $lastPoint        = $nowPoint;
        };

        $lastPoint = null;
        foreach ($groupExcursionPoint as $index => $nowPoint) {
            $nowPoint->normalAvgRate = $normalAvgRate;
            if (is_null($lastPoint)) {
                $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_1, $lastPoint);
            } else {
                $nowPoint->lastPointTime = $nowPoint->minTracePointTime - $lastPoint->maxTracePointTime;
                if ($nowPoint->lastPointTime > $maxReportingInterval) {
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_4, $lastPoint);
                    continue;
                }

                if (empty($nowPoint["longitude"]) || empty($nowPoint["latitude"])) {
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_5, $lastPoint);
                    continue;
                }

                if ((empty($lastPoint["longitude"]) || empty($lastPoint["latitude"]))) {
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_1, $lastPoint);
                    continue;
                }

                $nowPoint->lastPointDistance = calcDistance(
                    $nowPoint["longitude"],
                    $nowPoint["latitude"],
                    $lastPoint["longitude"],
                    $lastPoint["latitude"]
                );


                $nowPoint->actuallyAvgRate = sprintf(
                    "%.2f",
                    $nowPoint->lastPointDistance / $nowPoint->lastPointTime
                );

                if ($nowPoint->actuallyAvgRate > $nowPoint->normalAvgRate) {
                    if ($groupExcursionPoint->offsetExists($index - 1) && $groupExcursionPoint->offsetExists($index - 2)) {
                        $midPoint   = $groupExcursionPoint->offsetGet($index - 1);
                        $firstPoint = $groupExcursionPoint->offsetGet($index - 2);
                        if ($midPoint->status == AttendanceDailyPointTraceModel::STATUS_2 && $firstPoint->status == AttendanceDailyPointTraceModel::STATUS_2) {
                            if ($midPoint->actuallyAvgRate > $firstPoint->actuallyAvgRate * 3 && $midPoint->actuallyAvgRate > $nowPoint->actuallyAvgRate * 3) {
                                $continueFunc($nowPoint, $lastPoint->status, $lastPoint);
                                continue;
                            }
                        }
                    }
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_2, $lastPoint);
                } else {
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_3, $lastPoint);
                }
            }
        }
        return $groupExcursionPoint;
    }

    /**
     * 轨迹状态分段
     *
     * @param Collection $confirmedTracePoints
     * @return Collection
     */
    public static function traceDivideByStatus(Collection $confirmedTracePoints): Collection
    {
        $quoteGroup = [];
        $lastPoint  = [];
        $tempTrace  = [];
        $confirmedTracePoints->where("status", ">", AttendanceDailyPointTraceModel::STATUS_0)
                             ->each(function (
                                 AttendanceDailyPointTraceModel $pointTraceModel
                             ) use (
                                 &$quote,
                                 &$lastStatus,
                                 &$lastPoint,
                                 &$pokeTrace,
                                 &$quoteGroup,
                                 &$tempTrace
                             ) {
                                 $pointTraceModel->tracePointTime = formatDate($pointTraceModel->tracePointTime);

                                 if ($lastPoint && $pointTraceModel->status != $lastPoint->status) {
                                     array_push($tempTrace, $quoteGroup);
                                     $quoteGroup = [];
                                 }
                                 array_push($quoteGroup, $pointTraceModel);
                                 $lastPoint = $pointTraceModel;
                             });

        if ($quoteGroup) {
            array_push($tempTrace, $quoteGroup);
        }
        return collect($tempTrace);
    }

    /**
     * 构建轨迹详细信息
     *
     * @param Collection $divideTrace
     * @return array
     */
    public static function buildTraceDetail(Collection $divideTrace): array
    {
        $lastTrace = [];
        return $divideTrace->map(function ($trace) use (&$lastTrace) {
            $traceGroup = collect($trace);
            $endTrace   = $nextFirstPoint = $traceGroup->last();

            if ($endTrace["status"] == AttendanceDailyPointTraceModel::STATUS_3) {
                $groupAddress = $traceGroup->groupBy("address")->map(function ($groupTrace) {
                    return [
                        "count" => $groupTrace->count(),
                        "point" => $groupTrace->first()
                    ];
                });
                if ($groupAddress->count() > 1 && $groupAddress->where("count", ">", 1)->count() > 0) {
                    $endTrace = $groupAddress->sortByDesc("count")->first()["point"];
                }
            }

            if (empty($lastTrace)) {
                $lastTrace = $traceGroup->first();
            } else {
                $traceGroup->push($lastTrace);
            }

            $returnTrace = [
                "point"           => $traceGroup->sortBy("tracePointTime")->map(function (
                    $point
                ) {
                    return collect($point)->only([
                        "id",
                        "tracePointTime",
                        "longitude",
                        "latitude",
                        "status",
                        "lastPointDistance",
                        "lastPointTime",
                        "actuallyAvgRate",
                        "normalAvgRate",
                        "locationType",
                        "locationTypeText",
                        "accuracy"
                    ])->toArray();
                })->values()->toArray(),
                "begin"           => [
                    "id"             => $lastTrace["id"],
                    "tracePointTime" => $lastTrace["tracePointTime"],
                    "longitude"      => $lastTrace["longitude"],
                    "latitude"       => $lastTrace["latitude"],
                    "dumpEnergy"     => $lastTrace["dumpEnergy"],
                    "status"         => $lastTrace["status"],
                    "address"        => $lastTrace["address"],
                    "statusText"     => AttendanceDailyPointTraceModel::STATUS_TEXT[$lastTrace["status"]],
                ],
                "end"             => [
                    "id"             => $endTrace["id"],
                    "tracePointTime" => $endTrace["tracePointTime"],
                    "longitude"      => $endTrace["longitude"],
                    "latitude"       => $endTrace["latitude"],
                    "dumpEnergy"     => $endTrace["dumpEnergy"],
                    "status"         => $endTrace["status"],
                    "address"        => $endTrace["address"],
                    "statusText"     => AttendanceDailyPointTraceModel::STATUS_SHOW_TEXT[$endTrace["status"]],
                ],
                "statusText"      => AttendanceDailyPointTraceModel::STATUS_SHOW_TEXT[$endTrace["status"]],
                "normalAvgRate"   => $endTrace["normalAvgRate"],
                "actuallyAvgRate" => $endTrace["actuallyAvgRate"],
                "ids"             => $traceGroup->pluck("id")->implode(","),
                "time"            => strtotime($nextFirstPoint["tracePointTime"]) - strtotime($lastTrace["tracePointTime"]),
                "distance"        => sprintf("%.2f", collect($trace)->sum("lastPointDistance"))
            ];
            $lastTrace   = $nextFirstPoint;
            return $returnTrace;
        })->values()->filter()->values()->toArray();
    }

    /**
     * @param object $params
     * @return array[]
     * @throws ValidationException
     */
    public static function fieldTraceV3(object $params): array
    {
        $dailyGatherModel = AttendanceDailyGatherModel::query()
                                                      ->where("userId", $params->uid)
                                                      ->where("date", $params->date)
                                                      ->first();

        $trace = [];
        if (!empty($dailyGatherModel)) {
            list($startTime, $endTime, $normalAvgRate, $maxReportingInterval, $accuracy) = self::getAttendanceFieldConfig($dailyGatherModel);
            $lastPoint                           = null;
            $attendanceDailyPointTraceCollection = AttendanceDailyPointTraceModel::getDailyTracePoint(
                $dailyGatherModel->id,
                $startTime,
                $endTime
            )->each(function (AttendanceDailyPointTraceModel $dailyPointTraceModel) use (&$lastPoint) {
                if ($lastPoint) {
                    if ($dailyPointTraceModel->tracePointTime - $lastPoint->tracePointTime <= 0) {
                        $dailyPointTraceModel->status = AttendanceDailyPointTraceModel::STATUS_0;
                    }
                }
                $lastPoint = $dailyPointTraceModel;
            })->where("status", ">", AttendanceDailyPointTraceModel::STATUS_0);

            //躁点聚合数组
            $mergeExcursionPointCollect = self::mergeExcursionPoint($attendanceDailyPointTraceCollection, $accuracy);
            //躁点合并为单点
            $groupExcursionPoint = self::groupExcursionPoint($mergeExcursionPointCollect);
            //确认点位状态
            $confirmedTracePoints = self::confirmPointStatus(
                $groupExcursionPoint,
                $maxReportingInterval,
                $normalAvgRate
            );
            //轨迹状态分段
            $divideTrace = self::traceDivideByStatus($confirmedTracePoints);
            //轨迹数据重组
            $trace = self::buildTraceDetail($divideTrace);
        }

        if (count($trace) > 1) {
            unset($trace[0]);
        }
        return [
            "trace" => array_values($trace)
        ];
    }


    /**
     * @param object $params
     * @return array[]
     * @throws ValidationException
     */
    public static function fieldTraceV2(object $params): array
    {
        $dailyGatherModel = AttendanceDailyGatherModel::query()
                                                      ->where("userId", $params->uid)
                                                      ->where("date", $params->date)
                                                      ->first();

        $trace = [];
        if (!empty($dailyGatherModel)) {
            $attendanceConfig                    = ConfigService::getConfigDetail("kms.user",
                "attendance")["value"]["field"];
            $startTime                           = strtotime($dailyGatherModel->date . " " . $attendanceConfig["startTimeFormat"]);
            $endTime                             = strtotime($dailyGatherModel->date . " " . $attendanceConfig["endTimeFormat"]);
            $normalAvgRate                       = sprintf(
                "%.2f",
                $attendanceConfig["statusConfig"]["radius"] / $attendanceConfig["statusConfig"]["time"]
            );
            $maxReportingInterval                = $attendanceConfig["uploadConfig"]["maxReportingInterval"];
            $attendanceDailyPointTraceCollection = AttendanceDailyPointTraceModel::query()
                                                                                 ->where("dailyGatherId",
                                                                                     $dailyGatherModel->id)
                                                                                 ->where("tracePointTime", ">=",
                                                                                     $startTime)
                                                                                 ->where("tracePointTime", "<=",
                                                                                     $endTime)
                                                                                 ->where("status", ">",
                                                                                     AttendanceDailyPointTraceModel::STATUS_0)
                                                                                 ->orderBy("id")
//                                                                                 ->limit(100)
                                                                                 ->get();

            $lastPoint    = null;
            $continueFunc = function (
                AttendanceDailyPointTraceModel $nowPoint,
                int $status,
                AttendanceDailyPointTraceModel &$lastPoint = null,
                bool $isExcursionPoint = false
            ) {
                $nowPoint->status = $status;
                if ($isExcursionPoint) {
                    $nowPoint->longitude = $lastPoint->longitude;
                    $nowPoint->latitude  = $lastPoint->latitude;
                }
                $lastPoint = $nowPoint;
            };
            foreach ($attendanceDailyPointTraceCollection as $index => &$nowPoint) {
                $nowPoint->normalAvgRate        = $normalAvgRate;
                $nowPoint->tracePointFormatTime = formatDate($nowPoint->tracePointTime);
                $nowPoint->lastPointDistance    = $nowPoint->lastPointTime = $nowPoint->actuallyAvgRate = 0;
                $nowPoint->status               = $nowPoint->locationType = $nowPoint->locationTypeText = null;
                $nowPoint->accuracy             = 0;
                $nowPoint->isExcursionPoint     = false;
                if (strlen($nowPoint->extra) > 0 && str_contains($nowPoint->extra, "locationType")) {
                    $extra                      = json_decode($nowPoint->extra, true);
                    $nowPoint->locationType     = $extra["locationType"] ?? null;
                    $nowPoint->locationTypeText = AttendanceDailyPointTraceModel::LOCATION_TYPE_TEXT[$nowPoint->locationType] ?? null;
                    $nowPoint->accuracy         = (int)$extra["accuracy"] ?? 0;
                    if (in_array($nowPoint->locationType, [
                            AttendanceDailyPointTraceModel::LOCATION_TYPE_2,
                            AttendanceDailyPointTraceModel::LOCATION_TYPE_4,
                            AttendanceDailyPointTraceModel::LOCATION_TYPE_0,
                            AttendanceDailyPointTraceModel::LOCATION_TYPE_9
                        ]) || $nowPoint->accuracy >= 40) {
                        $nowPoint->isExcursionPoint = true;
                    }
                }


                if (is_null($lastPoint)) {
                    $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_1, $lastPoint);
                } else {
                    $nowPoint->lastPointTime = $nowPoint->tracePointTime - $lastPoint->tracePointTime;
                    if ($nowPoint->lastPointTime > $maxReportingInterval) {
                        $continueFunc(
                            $nowPoint,
                            AttendanceDailyPointTraceModel::STATUS_4,
                            $lastPoint,
                            $lastPoint->isExcursionPoint && $nowPoint->isExcursionPoint
                        );
                        continue;
                    }

                    if ($nowPoint->isExcursionPoint) {
                        if ($lastPoint->status == AttendanceDailyPointTraceModel::STATUS_1) {
                            $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_3, $lastPoint, true);
                        } else {
                            $continueFunc($nowPoint, $lastPoint->status, $lastPoint, true);
                        }
                        continue;
                    }

                    if (empty($nowPoint["longitude"]) || empty($nowPoint["latitude"])) {
                        $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_5, $lastPoint);
                        continue;
                    }

                    if ((empty($lastPoint["longitude"]) || empty($lastPoint["latitude"]))) {
                        $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_1, $lastPoint);
                        continue;
                    }

                    $nowPoint->lastPointDistance = calcDistance(
                        $nowPoint["longitude"],
                        $nowPoint["latitude"],
                        $lastPoint["longitude"],
                        $lastPoint["latitude"]
                    );


                    $nowPoint->actuallyAvgRate = sprintf(
                        "%.2f",
                        $nowPoint->lastPointDistance / $nowPoint->lastPointTime
                    );

                    if ($nowPoint->actuallyAvgRate > $nowPoint->normalAvgRate) {
                        if ($attendanceDailyPointTraceCollection->offsetExists($index - 1) && $attendanceDailyPointTraceCollection->offsetExists($index - 2)) {
                            $midPoint   = $attendanceDailyPointTraceCollection->offsetGet($index - 1);
                            $firstPoint = $attendanceDailyPointTraceCollection->offsetGet($index - 2);
                            if ($midPoint->status == AttendanceDailyPointTraceModel::STATUS_2 && $firstPoint->status == AttendanceDailyPointTraceModel::STATUS_2) {
                                if ($midPoint->actuallyAvgRate > $firstPoint->actuallyAvgRate * 3 && $midPoint->actuallyAvgRate > $nowPoint->actuallyAvgRate * 3) {
                                    $continueFunc($nowPoint, $lastPoint->status, $lastPoint);
                                    continue;
                                }
                            }
                        }
                        $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_2, $lastPoint);
                    } else {
                        $continueFunc($nowPoint, AttendanceDailyPointTraceModel::STATUS_3, $lastPoint);
                    }
                }
            }

            $quoteGroup = [];
            $lastPoint  = [];
            $tempTrace  = [];
            $attendanceDailyPointTraceCollection->where("status", ">", AttendanceDailyPointTraceModel::STATUS_0)
                                                ->each(function (
                                                    AttendanceDailyPointTraceModel $pointTraceModel
                                                ) use (
                                                    &$quote,
                                                    &$lastStatus,
                                                    &$lastPoint,
                                                    &$pokeTrace,
                                                    &$quoteGroup,
                                                    &$tempTrace
                                                ) {
                                                    $pointTraceModel->tracePointTime = formatDate($pointTraceModel->tracePointTime);

                                                    if ($lastPoint && $pointTraceModel->status != $lastPoint->status) {
                                                        array_push($tempTrace, $quoteGroup);
                                                        $quoteGroup = [];
                                                    }
                                                    array_push($quoteGroup, $pointTraceModel->toArray());
                                                    $lastPoint = $pointTraceModel;
                                                });

            if ($quoteGroup) {
                array_push($tempTrace, $quoteGroup);
            }

            $lastTrace = [];
            $trace     = collect($tempTrace)->map(function ($trace) use (&$lastTrace, $normalAvgRate) {
                $traceGroup = collect($trace);
                $endTrace   = $nextFirstPoint = $traceGroup->last();

                if ($endTrace["status"] == AttendanceDailyPointTraceModel::STATUS_3) {
                    $groupAddress = $traceGroup->groupBy("address")->map(function ($groupTrace) {
                        return [
                            "count" => $groupTrace->count(),
                            "point" => $groupTrace->first()
                        ];
                    });
                    if ($groupAddress->count() > 1 && $groupAddress->where("count", ">", 1)->count() > 0) {
                        $endTrace = $groupAddress->sortByDesc("count")->first()["point"];
                    }
                }

                if (empty($lastTrace)) {
                    $lastTrace = $traceGroup->first();
                } else {
                    $traceGroup->push($lastTrace);
                }


                $returnTrace = [
                    "point"           => $traceGroup->sortBy("tracePointTime")->map(function (
                        $point
                    ) {
                        return collect($point)->only([
                            "id",
                            "tracePointTime",
                            "longitude",
                            "latitude",
                            "status",
                            "lastPointDistance",
                            "lastPointTime",
                            "actuallyAvgRate",
                            "normalAvgRate",
                            "locationType",
                            "locationTypeText",
                            "accuracy"
                        ])->toArray();
                    })->values()->toArray(),
                    "begin"           => [
                        "id"             => $lastTrace["id"],
                        "tracePointTime" => $lastTrace["tracePointTime"],
                        "longitude"      => $lastTrace["longitude"],
                        "latitude"       => $lastTrace["latitude"],
                        "dumpEnergy"     => $lastTrace["dumpEnergy"],
                        "status"         => $lastTrace["status"],
                        "address"        => $lastTrace["address"],
                        "statusText"     => AttendanceDailyPointTraceModel::STATUS_TEXT[$lastTrace["status"]],
                    ],
                    "end"             => [
                        "id"             => $endTrace["id"],
                        "tracePointTime" => $endTrace["tracePointTime"],
                        "longitude"      => $endTrace["longitude"],
                        "latitude"       => $endTrace["latitude"],
                        "dumpEnergy"     => $endTrace["dumpEnergy"],
                        "status"         => $endTrace["status"],
                        "address"        => $endTrace["address"],
                        "statusText"     => AttendanceDailyPointTraceModel::STATUS_SHOW_TEXT[$endTrace["status"]],
                    ],
                    "statusText"      => AttendanceDailyPointTraceModel::STATUS_SHOW_TEXT[$endTrace["status"]],
                    "normalAvgRate"   => $normalAvgRate,
                    "actuallyAvgRate" => $endTrace["actuallyAvgRate"],
                    "ids"             => $traceGroup->pluck("id")->implode(","),
                    "time"            => strtotime($nextFirstPoint["tracePointTime"]) - strtotime($lastTrace["tracePointTime"]),
                    "distance"        => sprintf("%.2f", collect($trace)->sum("lastPointDistance"))
                ];
                $lastTrace   = $nextFirstPoint;
                return $returnTrace;
            })->values()->filter()->values()->toArray();
        }

        if (count($trace) > 0) {
            unset($trace[0]);
        }
        return [
            "trace" => array_values($trace)
        ];
    }


    /**
     * @param int $status
     * @param string $departmentId
     * @param string $jobTitle
     * @param string $name
     * @param int|null $fieldSwitch
     * @param array $uid
     */
    public static function fieldSwitch(
        int $status,
        string $departmentId = "",
        string $jobTitle = "",
        string $name = "",
        int $fieldSwitch = null,
        array $uid = []
    ) {
        $userList = KmsUser::query()->when($departmentId, function ($query) use ($departmentId) {
            $query->where("rootDepartmentId", $departmentId);
        })->when($jobTitle, function ($query) use ($jobTitle) {
            $query->where("jobTitle", $jobTitle);
        })->when($name, function ($query) use ($name) {
            $query->where(function ($query) use ($name) {
                $query->where("name", "like", "%$name%")->orWhere("nickname", "like", "%$name%");
            });
        })->when(!is_null($fieldSwitch), function ($query) use ($fieldSwitch) {
            $kmsUserConfig = KmsUserConfigModel::query()
                                               ->where("fieldSwitch", KmsUserConfigModel::FIELD_SWITCH_ON)
                                               ->get();
            if ($fieldSwitch) {
                $query->whereIn("rmsUserId", $kmsUserConfig->pluck("userId")->toArray());
            } else {
                $query->whereNotIn("rmsUserId", $kmsUserConfig->pluck("userId")->toArray());
            }
        })->when(count($uid), function ($query) use ($uid) {
            $query->whereIn("rmsUserId", $uid);
        })->where("rmsUserId", ">", 0)->get();

        if ($userList) {
            $userConfigList = KmsUserConfigModel::query()->whereIn("userId", $userList->pluck("rmsUserId")->toArray())
                                                ->get();
            if ($userConfigList->isNotEmpty()) {
                KmsUserConfigModel::query()->whereIn("userId", $userConfigList->pluck("userId")->toArray())->update([
                    "updatedAt"    => time(),
                    "updatedByUid" => request()->user()->id,
                    "fieldSwitch"  => $status
                ]);
            }
            $notExistsUserIds = array_diff(
                $userList->pluck("rmsUserId")->toArray(),
                $userConfigList->pluck("userId")->toArray(),
            );

            if ($notExistsUserIds) {
                $insertData = [];
                collect($notExistsUserIds)->unique()->map(function ($uid) use (&$insertData, $status) {
                    array_push($insertData, [
                        "userId"       => $uid,
                        "fieldSwitch"  => $status,
                        "createdAt"    => time(),
                        "createdByUid" => request()->user()->id
                    ]);
                });
                KmsUserConfigModel::query()->insert($insertData);
            }
        }
    }

    /**
     * @param object $params
     */
    public static function punchShare(object $params)
    {
        event(
            new PunchShareEvent(
                $params->punchType,
                $params->remark,
                $params->resourceId,
                $params->groupIds,
                auth()->user()->toArray()
            )
        );
    }

    /**
     * 获取开店 - 组织管理-省总/城市经理关联的考勤用户
     * @param int $uid
     * @return array
     */
    public static function attendanceUser(int $uid): array
    {
        $data = [];

        //省总
        $manageIds = OpenShopOrganizationManageModel::query()
            ->where('operationUid', $uid)
            ->where('type', OpenShopOrganizationManageModel::TYPE_PROVINCE)
            ->get()
            ->pluck('id')->values()->unique()->toArray();

        //城市经理
        $cityManageIds = OpenShopOrganizationManageUserModel::query()
            ->where('userId', $uid)
            ->where('type', OpenShopOrganizationManageUserModel::TYPE_CITY_MANAGER)
            ->get()
            ->pluck('manageId')->values()->unique()->toArray();

        $manageIds = array_merge($manageIds, $cityManageIds);
        if ($manageIds) {
            $manageIds = array_unique($manageIds);
            OpenShopOrganizationManageModel::query()
                ->whereIn('id', $manageIds)
                ->get()
                ->map(
                    function ($item) use (&$data) {
                        if ($item['attendanceUser']) {
                            $data[$item['name']] = json_decode($item['attendanceUser'], true) ?: [];
                        }
                    }
                );
        }
        if ($data) {
            $userIds = [];
            foreach ($data as $k => $v) {
                if ($v && is_array($v)) {
                    $userIds = array_merge($userIds, array_unique(array_column($v, 'uid')));
                }
            }
            if ($userIds) {
                //飞书用户信息
                $userInfos = KmsUser::query()->whereIn("rmsUserId", $userIds)
                    ->get()->keyBy('rmsUserId')->toArray();
                //用户当天轨迹信息
                $attendanceDailyGatherData = AttendanceDailyGatherModel::query()
                    ->whereIn("userId", $userIds)
                    ->where("date", date('Y-m-d'))
                    ->get()->keyBy('userId')->toArray();
                foreach ($data as &$v) {
                    if ($v && is_array($v)) {
                        foreach ($v as &$user) {
                            //考勤状态 -1 - 无考勤 0 - 异常 1 - 正常
                            $status                            = $attendanceDailyGatherData[$user['uid']]['status'] ?? AttendanceDailyGatherModel::STATUS_NO_TRACE;
                            $user['jobTitle']                  = $userInfos[$user['uid']]['jobTitle'] ?? '';
                            $user['avatar']                    = $userInfos[$user['uid']]['avatar'] ?? '';
                            $user['attendanceDailyStatus']     = $status;
                            $user['attendanceDailyStatusText'] = AttendanceDailyGatherModel::STATUS_TEXT[$status] ?? "无轨迹";
                        }
                    }
                }
            }
        }
        return $data;
    }

    /**
     * 查看当前用户是否有开店 - 组织管理-省总/城市经理权限
     * @param int $uid
     * @return array
     */
    public static function attendanceUserCheck(int $uid): array
    {
        $data['isCheck'] = false;

        //省总
        $count = OpenShopOrganizationManageModel::query()
            ->where('operationUid', $uid)
            ->where('type', OpenShopOrganizationManageModel::TYPE_PROVINCE)
            ->count();
        if ($count) {
            $data['isCheck'] = true;
            return $data;
        }

        //城市经理
        $count = OpenShopOrganizationManageUserModel::query()
            ->where('userId', $uid)
            ->where('type', OpenShopOrganizationManageUserModel::TYPE_CITY_MANAGER)
            ->count();
        if ($count) {
            $data['isCheck'] = true;
            return $data;
        }
        return $data;
    }
}
